home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1999 November
/
Macworld (1999-11).dmg
/
Updaters
/
WhiteCap 3.0.4
/
WhiteCap Source.sit
/
WhiteCap Source
/
WhiteCapWorld.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1999-08-28
|
16KB
|
610 lines
#include "WhiteCapWorld.h"
// Praise Jesus! I can't keep quiet...Ye-haw!
#include "Sample.h"
#include "CEgIFile.h"
#include "ArgList.h"
#include "R3Matrix.h"
#if EG_MAC
#include <Dialogs.h>
#endif
#if EG_WIN
#include "RectUtils.h"
#endif
#include "PixPort.h"
#include "EgOSUtils.h"
#include "CEgFileSpec.h"
WhiteCapWorld::WhiteCapWorld() {
mSamples = NULL;
mRecentSample = NULL;
mWave = NULL;
mTransitionTime = -1;
mPi = 3.1415926535897;
mCurBackClr = -1; // This will cause an update to mCurBackClr in Render()
mNumSampleBins = NUM_SAMPLE_BINS;
mDict.AddVar( "S", &mS );
mDict.AddVar( "T", &mT );
mDict.AddVar( "ST", &mST );
mDict.AddVar( "DT", &mDT );
mDict.AddVar( "PI", &mPi );
mDict.AddVar( "NUM_SAMPLE_BINS", &mNumSampleBins ); // Let user read num sample bins if they want
mDict.AddVar( "BASS1", &mBass1 );
mDict.AddVar( "BASS2", &mBass2 );
mDict.AddVar( "BASS3", &mBass3 );
mDict.AddFcn( "MAG", &mMagPtr, NUM_SAMPLE_BINS );
SetRect( &mPaneRect, 5000, 5000, -5000, -5000 );
mLastSampleTime = EgOSUtils::CurTimeMS();
}
WhiteCapWorld::~WhiteCapWorld() {
Sample *sample;
// Move all the sample to the mFreeList
ExpireSamples();
// Delete all the sample from the free list
while ( mFreeList.FetchLast( &sample ) ) {
delete sample;
mFreeList.RemoveLast();
}
}
#define __FACTORY "\
Resn=10,\
Durn=\".8\",\
CamX=\"59*cos(t/6)\",\
CamY=\"50*sin(t/7)\",\
CamZ=\"25 + 10*cos(t/13)\",\
CmLX=\"40\",CmLY=\"0\",CmLZ=\"0\",\
CUpX=\"0\",CUpY=\"0\",CUpZ=\"1\",\
R=\"0\",G=\"1-dt\",B=\"0\",\
LvlR=\"1\",LvlG=\"0\",LvlB=\"0\",\
widt=320,heig=300,\
ConL=1,ConB=1,\
Fall=\"0.035\",\
X=\"100*dt\",Y=\"130*s-65\",Z=\"20*abs( mag( s ) )\",\
Scal=\"600\",\
ScSz=1,\
Vers=30,\
Pers=\"350\""
void WhiteCapWorld::Init( const CEgFileSpec* inConfig, long inTransitionTime ) {
CEgIFile file;
int ok, vers;
UtilStr str, configText, num;
ArgList args;
file.open( inConfig );
args.SetArgs( &file );
ok = file.noErr();
if ( ok ) {
vers = args.GetArg( 'Vers' );
ok = vers == 30;
}
// If something went wrong or we have an earlier version, default to the factory config
if ( ! ok ) {
args.Clear();
args.SetArgs( __FACTORY );
}
mTransitionTime = inTransitionTime;
mTransitionEnd = EgOSUtils::CurTimeMS() + mTransitionTime;
// If first time load, don't do any transition
if ( mWave == NULL ) {
mWave = &mWave1;
mNextWave = &mWave2;
mWave -> Assign( args, mDict );
mTransitionTime = -1;
inConfig -> GetFileName( mWave -> mTitle ); }
else {
mNextWave -> Assign( args, mDict );
mWave -> SetupTransition( mNextWave, &mTransitionT );
inConfig -> GetFileName( mNextWave -> mTitle );
}
}
void WhiteCapWorld::SetPaneRect( const Rect& inRect ) {
int width = inRect.right - inRect.left;
int heigt = inRect.bottom - inRect.top;
mPaneRect = inRect;
// Besure we erase the new rect are next time
mRenderedRect = mPaneRect;
// Invaliadate this rect till later...
::SetRect( &mTitleRect, 0, 0, 0, 0 );
// If the scale changes with window size
mWave -> SetScaleToFit( width, heigt );
mNextWave -> SetScaleToFit( width, heigt );
}
#define __Chk( xx, yy ) x = xx; y = yy; \
if ( x >= mRenderedRect.right ) \
mRenderedRect.right = x+1; \
if ( x <= mRenderedRect.left ) \
mRenderedRect.left = x-1; \
if ( y >= mRenderedRect.bottom ) \
mRenderedRect.bottom = y+1; \
if ( y <= mRenderedRect.top ) \
mRenderedRect.top = y-1;
#define _evalClr( var, clr ) { clrTemp = 65534.9 * mWave -> clr.Evaluate(); if ( clrTemp < 0 ) clrTemp = 0; if ( clrTemp <= 0xFFFF ) var = clrTemp; else var = 0xFFFF; }
// Pre: Render() was called before this
void WhiteCapWorld::DrawConfigName( void* inPort ) {
RGBColor clr;
float w;
unsigned char* str;
// Save the current mac port. Even Win9X/NT is crap, it's APIs/Interfaces are pretty decent, namely the GDI stuff.
// But sure, then again, if an OS designed for 1984ish cpus can compete w/ a 'modern' OS, i'd be damned
// ashamed if i was on the Win implementation team...
#if EG_MAC
GrafPtr savePort;
::GetPort( &savePort );
::SetPort( (GrafPtr) inPort );
::TextFont( 20 );
::TextSize( 12 );
#endif
#if EG_WIN
HDC hdc = ::GetDC( (HWND) inPort );
#endif
mTitleRect.left = mPaneRect.left;
mTitleRect.bottom = mPaneRect.bottom;
mTitleRect.top = mPaneRect.bottom - 20;
if ( mTransitionTime > 0 ) {
if ( mTransitionT > .5 ) {
w = 2 * mTransitionT - 1;
str = mWave -> mTitle.getPasStr(); }
else {
w = 1 - 2 * mTransitionT;
str = mNextWave -> mTitle.getPasStr();
}
clr.red = mCurBackClrRGB.red + w * ( mTextColor.red - mCurBackClrRGB.red );
clr.green = mCurBackClrRGB.green + w * ( mTextColor.green - mCurBackClrRGB.green );
clr.blue = mCurBackClrRGB.blue + w * ( mTextColor.blue - mCurBackClrRGB.blue );
#if EG_MAC
::RGBForeColor( &clr );
::MoveTo( mTitleRect.left + 4, mTitleRect.bottom - 4 );
::DrawString( str );
#else
::SetTextColor( hdc, __ClrREF( clr ) );
::SetBkMode( hdc, TRANSPARENT );
::TextOut( hdc, mTitleRect.left + 4, mTitleRect.top, (char*) &str[ 1 ], str[ 0 ] );
#endif
}
else {
#if EG_MAC
::RGBForeColor( &mTextColor );
::MoveTo( mTitleRect.left + 4, mTitleRect.bottom - 4 );
::DrawString( mWave -> mTitle.getPasStr() );
#else
::SetTextColor( hdc, __ClrREF( mTextColor ) );
::SetBkMode( hdc, TRANSPARENT );
::TextOut( hdc, mTitleRect.left + 4, mTitleRect.top, mWave -> mTitle.getCStr(), mWave -> mTitle.length() );
#endif
}
#if EG_MAC
// Don't screw up future calls to copybits. Wtf--someone tell my my copyBits() freaks out when
// the fore color isn't black
clr.red = clr.green = clr.blue = 0;
::RGBForeColor( &clr );
Point pt;
::GetPen( &pt );
mTitleRect.right = pt.h;
// Restore the prev mac port
::SetPort( savePort );
#endif
#if EG_WIN
mTitleRect.right = 250; //!!! ###
::ReleaseDC( (HWND) inPort, hdc );
#endif
}
#define __applySampleToGlobals( s ) mMagPtr = s -> mSample; \
mST = .001 * s -> mSampleTime; \
mDT = ( (float) ( mLastSampleTime - s -> mSampleTime ) ) / ( (float) mWave -> mSampleDuration ); \
mBass1 = s -> mBass[ 0 ]; \
mBass2 = s -> mBass[ 1 ]; \
mBass3 = s -> mBass[ 2 ];
void WhiteCapWorld::Render( long inCurTime, PixPort& inPort, Rect& outDirtyRect ) {
V3 pt;
long x, y, curBlurNum, p_x, p_y, i, clrTemp;
short xorg = ( mPaneRect.right + mPaneRect.left ) / 2;
short yorg = ( mPaneRect.bottom + mPaneRect.top ) / 2;
R3Matrix T;
float stepSize, f_x, f_y, f_z;
Sample* sample;
long borderXtra;
RGBColor curClr;
long curWidth, prevWidth;
bool transition;
static ScrnPt sPt[ NUM_SAMPLE_BINS ];
// Calculate the position, direction, and rotation angle of the camera
mT = ((float) inCurTime ) / 1000.0;
transition = mTransitionTime > 0;
if ( transition ) {
f_x = (float) ( mTransitionEnd - inCurTime ) / ( (float) mTransitionTime );
if ( f_x < 0 )
f_x = 0;
mTransitionT = pow( f_x, TRANSITION_ALPHA );
mWave -> SetupFrame( mNextWave, mTransitionT );
}
// Before we eval all the "B" exprs, it's possiblle they use the mag() fcn--in B vars,
// it references the most recent sample. Warning, if there's no samples available, any B
// vars that call mag() will get junk.
if ( mSamples ) {
__applySampleToGlobals( mSamples ) }
else {
mMagPtr = (float*) sPt;
mBass1 = 0;
mBass2 = 0;
mBass3 = 0;
}
// Eval all the "B" exprs
mWave -> mB_Var.Evaluate();
if ( transition )
mNextWave -> mB_Var.Evaluate();
// Evaluate the current background color
_evalClr( mCurBackClrRGB.red, mBackR )
_evalClr( mCurBackClrRGB.green, mBackG )
_evalClr( mCurBackClrRGB.blue, mBackB )
clrTemp = inPort.SetBackColor( mCurBackClrRGB );
if ( clrTemp != mCurBackClr ) {
mCurBackClr = clrTemp;
mRenderedRect = mPaneRect;
}
// The Camera pos
mCamera.mPos.mX = mWave -> mCamX.Evaluate();
mCamera.mPos.mY = mWave -> mCamY.Evaluate();
mCamera.mPos.mZ = mWave -> mCamZ.Evaluate();
// The Camera look direction
mCamera.mDir.mX = mWave -> mCamLX.Evaluate();
mCamera.mDir.mY = mWave -> mCamLY.Evaluate();
mCamera.mDir.mZ = mWave -> mCamLZ.Evaluate();
mCamera.mDir.subtract( mCamera.mPos );
// The Camera "up" direction
mCamera.mUpDir.mX = mWave -> mCamUpX.Evaluate();
mCamera.mUpDir.mY = mWave -> mCamUpY.Evaluate();
mCamera.mUpDir.mZ = mWave -> mCamUpZ.Evaluate();
mCamera.mXYScale = mWave -> mXYScale;
// Calc the main transformation matrix, T
mCamera.CalcTransMatrix( T );
// Erase the area that has stuff drawn on it
outDirtyRect = mRenderedRect;
inPort.EraseRect( &mRenderedRect );
mRenderedRect.top = 5000; mRenderedRect.left = 5000;
mRenderedRect.bottom = -5000; mRenderedRect.right = -5000;
/*
// Line diagnostic
for ( float ang = 0; ang < 2*PI; ang += .3 ) {
x = xorg + 50 * cos( ang + .05 * mT );
y = yorg - 50 * sin( ang + .05 * mT );
p_x = xorg + 150 * cos( ang + .05 * mT );
p_y = yorg - 150 * sin( ang + .05 * mT );
inPort.SetLineWidth( ang * 2 );
inPort.Line( x, y, p_x, p_y, 0x000A0A0A );
inPort.SetLineWidth( 1 );
inPort.Line( x, y, p_x, p_y, 0x000A000A );
}
outDirtyRect.left = xorg - 160;
outDirtyRect.right = xorg + 160;
outDirtyRect.top = yorg - 160;
outDirtyRect.bottom = yorg + 160;
mRenderedRect = outDirtyRect;
return; */
// Draw each sample to the screen, starting from the oldest sample
curBlurNum = mWave -> mNumBlurs;
// This gets bigger when the linewidth gets bigger
borderXtra = 0;
// Make sure t will never be > 1.0
discardExpiredRows( inCurTime );
// Cache stuff that gets looked up often
bool x_depS, y_depS, z_depS, r_depS, g_depS, b_depS;
x_depS = mWave -> mX_Dep_S;
y_depS = mWave -> mY_Dep_S;
z_depS = mWave -> mZ_Dep_S;
r_depS = mWave -> mR_Dep_S;
g_depS = mWave -> mG_Dep_S;
b_depS = mWave -> mB_Dep_S;
// This is how many pieces we chop up s's interval from 0 to 1
stepSize = 1.0 / ( (float) ( mWave -> mNum_S_Steps ) );
for ( sample = mSamples; sample; sample = sample -> mNext ) {
// Link mWave.mDict1 to the sample's wave data
// Assign the delta-time index (in secs) for this sample
__applySampleToGlobals( sample )
// Evaluate all the "C" expressions
mWave -> mC_Var.Evaluate();
if ( transition )
mNextWave -> mC_Var.Evaluate();
// Calc how wide this sample's linewidth is (round up if > .5)
prevWidth = curWidth;
curWidth = ( mWave -> mLineWidth.Evaluate() + 0.5 );
if ( curWidth > borderXtra )
borderXtra = curWidth;
inPort.SetLineWidth( curWidth );
// Evaluate vars that are indep of S (or D)
if ( ! x_depS ) f_x = mWave -> mX.Evaluate() - mCamera.mPos.mX;
if ( ! y_depS ) f_y = mWave -> mY.Evaluate() - mCamera.mPos.mY;
if ( ! z_depS ) f_z = mWave -> mZ.Evaluate() - mCamera.mPos.mZ;
if ( ! r_depS ) _evalClr( curClr.red, mR )
if ( ! g_depS ) _evalClr( curClr.green, mG )
if ( ! b_depS ) _evalClr( curClr.blue, mB )
// Draw the old samples to the new ones
for ( mS = 0, i = 0; i < mWave -> mNum_S_Steps; i++, mS += stepSize ) {
// Evaluate all the "D" expressions
mWave -> mD_Var.Evaluate();
if ( transition )
mNextWave -> mD_Var.Evaluate();
// Only evaluate expressions that are dependent on S
pt.mX = ( x_depS ) ? mWave -> mX.Evaluate() - mCamera.mPos.mX : f_x;
pt.mY = ( y_depS ) ? mWave -> mY.Evaluate() - mCamera.mPos.mY : f_y;
pt.mZ = ( z_depS ) ? mWave -> mZ.Evaluate() - mCamera.mPos.mZ : f_z;
pt.transform( T, mWave -> mPerspectiveInt );
p_x = xorg + pt.mX;
p_y = yorg - pt.mY;
// Calc the screen pt and update out bounds rectangle
__Chk( p_x, p_y )
// Calculate the color for this point
if ( r_depS ) _evalClr( curClr.red, mR )
if ( g_depS ) _evalClr( curClr.green, mG )
if ( b_depS ) _evalClr( curClr.blue, mB )
// Are we supposed to connect to the prev sample? Also catch when we're drawin the first sample
if ( mWave -> mConnectSamples ) {
if ( sample != mSamples ) {
if ( sample -> mNext )
inPort.Line( sPt[ i ].x, sPt[ i ].y, p_x, p_y, sPt[ i ].color, curClr );
else {
inPort.SetLineWidth( prevWidth );
inPort.Line( sPt[ i ].x, sPt[ i ].y, p_x, p_y, sPt[ i ].color, curClr );
inPort.SetLineWidth( curWidth );
}
}
}
// Record key scrn info for current sample
sPt[ i ].x = p_x;
sPt[ i ].y = p_y;
// Evaluate the level color (if it was given)
if ( sample -> mNext )
sPt[ i ].color = curClr;
else {
_evalClr( sPt[ i ].color.red, mLvlR )
_evalClr( sPt[ i ].color.green, mLvlG )
_evalClr( sPt[ i ].color.blue, mLvlB )
}
// If we're connecting bins...
if ( mWave -> mConnectBins && i > 0 )
inPort.Line( p_x, p_y, sPt[ i - 1 ].x, sPt[ i - 1 ].y, sPt[ i ].color, sPt[ i - 1 ].color );
// If we're just drawing dots... (we can skip drawing dots if samples are already connected)
else if ( ! mWave -> mConnectSamples || ! sample -> mNext )
inPort.Line( p_x, p_y, p_x, p_y, sPt[ i ].color, sPt[ i ].color );
}
// Connect bin 0 and bin N is user wants it
if ( mWave -> mConnectFirstLast ) {
curClr = sPt[ mWave -> mNum_S_Steps - 1 ].color;
inPort.Line( p_x, p_y, sPt[ 0 ].x, sPt[ 0 ].y, curClr, curClr );
}
// Do the blur if the number of blurs is valid
if ( mWave -> mNumBlurs > 0 ) {
float t_scale = 1000.0 * mDT / mWave -> mSampleDuration;
if ( t_scale <= ( (float) curBlurNum - 1.0) / mWave -> mNumBlurs ) {
::InsetRect( &mRenderedRect, - borderXtra - 3, - borderXtra - 3 );
inPort.GaussBlur( mWave -> mBlurVal, mRenderedRect, NULL );
curBlurNum--;
}
}
}
// We'll let the text color be the color of the s == 1 side of the level
if ( mSamples )
mTextColor = sPt[ 0 ].color;
else {
// cosmetic prob: when we don't have any samples, we don't have a clr for the text.
// What do we do? for now, we'll just use the text color as the last from (bad)
}
// If we've just finished a transition, end it
if ( transition && inCurTime > mTransitionEnd ) {
WC_WaveShape* temp = mWave;
mWave = mNextWave;
mNextWave = temp;
mTransitionTime = -1;
mRenderedRect = mPaneRect; // Refresh everthing
}
// Things like line widths > 1 may have drawn a little outside our bounds rect
::InsetRect( &mRenderedRect, -borderXtra - 1, -borderXtra - 1 );
// The area we have to refresh either has new bits or old bits
::UnionRect( &mRenderedRect, &outDirtyRect, &outDirtyRect );
// Limit the refresh rect to our pane
::SectRect( &mPaneRect, &outDirtyRect, &outDirtyRect );
}
void WhiteCapWorld::RefreshRect( const Rect& inRect ) {
Rect r;
// Clip the rect to this pane
::SectRect( &mPaneRect, &inRect, &r );
// Make sure we erase that part of the window next time
::UnionRect( &mRenderedRect, &r, &mRenderedRect );
}
void WhiteCapWorld::RecordSample( long inCurTime, const float inSpectrum[] ) {
Sample* sample;
// Store the newest sample, don't store anything if its less than our time resolution
if ( inCurTime - mLastSampleTime > mWave -> mSampleDelay ) {
// Get a free sample structure
if ( mFreeList.FetchLast( &sample ) )
mFreeList.RemoveLast();
else
sample = new Sample();
// Maintain our list of samples
sample -> mNext = NULL;
if ( mRecentSample )
mRecentSample -> mNext = sample;
else
mSamples = sample;
// Set the sample's data
sample -> Assign( inCurTime, inSpectrum, mRecentSample, mWave -> mFalloff );
// Update "current" sample shortcuts
mRecentSample = sample;
mLastSampleTime = inCurTime;
}
}
void WhiteCapWorld::discardExpiredRows( long inCurTime ) {
long dur = mWave -> mSampleDuration;
Sample* sample = mSamples;
while ( sample ) {
// Step from old to newer samples, discarding until we don't have old samples
if ( sample -> TimeOfSample() + dur < inCurTime ) {
if ( sample == mRecentSample )
mRecentSample = NULL;
mFreeList.Add( sample );
sample = sample -> mNext;
mSamples = sample; }
else
sample = NULL;
}
}
void WhiteCapWorld::ExpireSamples() {
Sample* sample = mSamples;
while ( sample ) {
mFreeList.Add( sample );
sample = sample -> mNext;
}
mSamples = NULL;
mRecentSample = NULL;
}